#ifndef AER_BLUREFFECT_HPP_
#define AER_BLUREFFECT_HPP_

#include "aerPostEffect.hpp"


namespace aer {
  
class BlurEffect : public PostEffect
{
  private:
    Program m_program;
    
    struct Input
    {
      Texture *pColor;
    } m_input;
 
    struct Blur
    {
      RenderTexture rt;
      Texture color;
    } m_hblur, m_vblur;
    
    // Params
    I32 m_kernelSize;
    Vector2 m_texelSize;
    
  public:
    BlurEffect()
    {
      m_kernelSize = 1;
      
      /// Inputs
      addInput( "Color", m_input.pColor);
      
      /// Outputs
      addOutput( "Color", m_vblur.color);
    }
    
    ~BlurEffect()
    {
      m_program.release();
      m_hblur.rt.release();
      m_hblur.color.release();
      m_vblur.rt.release();
      m_vblur.color.release();
    }    
    
    
    void init()
    {
      static const char *fShader = AER_STRINGIFY(
                    \x23 version 330\n

                    // IN
                    in vec2 vTexCoord;

                    // OUT
                    layout(location = 0) out vec4 fragColor;

                    // UNIFORM
                    uniform sampler2D uInputTex;

                    uniform bool uHorizontal;
                    uniform vec2 uBlurDir;
                    uniform int uKernelSize = 8;

                    // ? blur alpha channel too ?
                    vec4 linearBlur()
                    {
                      vec4 blurColor = vec4(0.0f);
                      float sum_weight = 0;

                      for (int i=-uKernelSize; i<=uKernelSize; ++i)
                      {
                        vec4 value = texture( uInputTex, vTexCoord + i * uBlurDir);

                        // weight, function of sample distance
                        float w = abs(i)/float(uKernelSize);//halfSize - abs(i) + 1;
                              w = 1.0f - w*w*(3.0f-2.0f*w);
                        
                        blurColor += w * value;	  
                        sum_weight += w;
                      }  
                      blurColor /= sum_weight;
                      
                      return blurColor;
                    }


                    void main()
                    {
                      fragColor = linearBlur();
                      //fragColor.a = 1.0f;
                    }
      );
      
      m_program.generate();
        m_program.addShaderSrc( Program::VERTEX_SHADER,   ScreenQuad::GetVertexShader());
        m_program.addShaderSrc( Program::FRAGMENT_SHADER, fShader);
      AER_CHECK( m_program.link() );
      
      m_hblur.color.generate();
      m_hblur.color.bind();    
        m_hblur.color.setFilters( Texture::LINEAR, Texture::LINEAR);
        m_hblur.color.setWraps( Texture::CLAMP_TO_EDGE, Texture::CLAMP_TO_EDGE);
      
      m_vblur.color.generate();
      m_vblur.color.bind();    
        m_vblur.color.setFilters( Texture::LINEAR, Texture::LINEAR);
        m_vblur.color.setWraps( Texture::CLAMP_TO_EDGE, Texture::CLAMP_TO_EDGE);
      
      Texture::Unbind( Texture::TEXTURE_2D, 0u);
      
      m_hblur.rt.generate();
      m_vblur.rt.generate();
    }
    
    void resize(const U32 width, const U32 height)
    {
      m_hblur.color.bind();
      m_hblur.color.resize( width, height, GL_RGBA8);      
      m_vblur.color.bind();
      m_vblur.color.resize( width, height, GL_RGBA8);
      Texture::Unbind( Texture::TEXTURE_2D, 0u);
      
      m_hblur.rt.bind();
      AER_CHECK( m_hblur.rt.setup( &m_hblur.color ) );      
      m_vblur.rt.bind();
      AER_CHECK( m_vblur.rt.setup( &m_vblur.color ) );
      RenderTarget::unbind();

      m_hblur.rt.resize( width, height);  //
      m_vblur.rt.resize( width, height);  //
      
      m_texelSize = Vector2( 1.0f/width, 1.0f/height);
    }
    
    void apply()
    {
      m_program.bind();
      m_program.setUniform( "uInputTex", 0);
      m_program.setUniform( "uKernelSize", m_kernelSize);      
      
      /// First pass : Horizontal blur
      m_program.setUniform( "uBlurDir", Vector2(m_texelSize.x, 0.0f));
      m_input.pColor->bind( 0u );
      m_hblur.rt.begin();
        ScreenQuad::Draw();
      m_hblur.rt.end();
            
      /// Second pass : Vertical blur
      m_program.setUniform( "uBlurDir", Vector2(0.0f, m_texelSize.y));
      m_hblur.color.bind( 0u );
      if (getTarget() == TEXTURE) { m_vblur.rt.begin(); }
        ScreenQuad::Draw();
      if (getTarget() == TEXTURE) { m_vblur.rt.end(); }
      
      Texture::Unbind( Texture::TEXTURE_2D, 0u);      
      m_program.unbind();
    }
    
    
    I32 getKernelSize() const { return m_kernelSize; }
    void setKernelSize(const I32 kernelSize) { m_kernelSize = kernelSize; }
};
  
} // aer

#endif //AER_BLUREFFECT_HPP_
